#include "./include/MFQ_t.h"
#include "./include/TCB_t.h"
#include "./include/M_printModule@windows.h"
#include <time.h>
#include <iostream>
#include <conio.h>

using namespace std;

#define  KERNEL_STATE  1    // 核心态
#define  USER_STATE    0    // 用户态

/*
 * 函数声明
 */ 
DWORD WINAPI thread_runing(LPVOID lpParamter);
void thread_gen();            // 线程发生器
void clock_interrupt();       // 模拟时钟中断
void interrupt_process();     // 模拟中断处理
void getCPU(ElemNode *pNode); // 获取CPU时间
void timeOut(int tid);        // 表示时间片运行完
void depriveCPU(int tid);     // 剥夺tid的CPU时间

void freethread();               // 释放线程
void freeResourse();             // 释放资源
int  check_high_priority(int k); // 查看有没有更高优先级的线程
int  random(int low, int hi);    // 返回一个[low, hi]之间的随机数

/*
 * 全局变量
 */
MFQ_t*    pMFQ=NULL;
ElemNode* pActiveNode=NULL;
TCB_t*    pActiveTCB=NULL;
TCB_t*    pTCBs[MAX_THREAD_SIZE+1]={};
HANDLE    threads_h[MAX_THREAD_SIZE+1]={};

int flag[MAX_THREAD_SIZE+1]={};
int last_thread=-1;        // 上一次运行的线程
int active_thread=-1;      // 当前运行的活跃线程
int slice_time_unit=1000;  // 每个时间片 1 秒
int state=USER_STATE;      // 运行状态，初始化为用户态
int exit_flag=0;           // 退出标志 

int main()
{
    initConsole(50+12*MAX_THREAD_SIZE, 40);
    pMFQ=createMFQ();
    time_t time_last,time_now;
    time(&time_last);
    thread_gen();
    pActiveNode=dispatch(pMFQ);
    getCPU(pActiveNode);

    while( !exit_flag ) {
        showMFQ(pMFQ);
        // 每隔5秒产生一个线程
        time(&time_now);
        if(time_now - time_last > 5) { 
            thread_gen();
            time_last=time_now;
        }
        // 调度线程执行
        if(state==KERNEL_STATE) {
            interrupt_process();
        }
        // 按任意键结束程序
        if( _kbhit() ) {
            exit_flag=1;
        }
    }

    freeResourse();
    cout << endl;
    return 0;
}

DWORD WINAPI thread_runing(LPVOID lpParamter)
{
    HANDLE curThread_h=threads_h[active_thread];
    if(lpParamter) {
        TCB_t* pTCB=(TCB_t *)lpParamter;
        int run_time=pTCB->run_time;
        int total_time=pTCB->total_time;
        while( run_time<total_time && !exit_flag ) {
            printLastThread();
            printNowThread();
            printNextThread();
            Sleep(slice_time_unit);
            /*
            * 每运行一个时间片
            * 就来一次时钟中断
            * 控制流进入主线程进行中断处理
            */
            clock_interrupt();
            run_time=pTCB->run_time; // 每运行一个时间片，更新 run_time  
        }
        // 线程运行结束，释放资源
        freethread();
    }
    if(curThread_h) {
        CloseHandle(curThread_h);
    }
}

void thread_gen()
{
    int id = random(0, MAX_THREAD_SIZE);
    if(0==flag[id]) {
        // 标志该位置为 1
        flag[id]=1;
        // 创建并初始化 TCB
        pTCBs[id] = createTCB();
        pTCBs[id]->tid = id;
        pTCBs[id]->name = (char)('A'+id);
        pTCBs[id]->priority = random(1,MFQ_MAXSIZE);  // 随机优先级
        pTCBs[id]->time_slice = random(5,10);         // 随机分配运行时间片
        pTCBs[id]->total_time = random(30,50);        // 随机初始化总的运行时间
        threads_h[id] = CreateThread(NULL,0,thread_runing,pTCBs[id],CREATE_SUSPENDED,NULL);
        // 封装结点
        ElemNode *pNode=createElemNode();
        pNode->pEelm = (void *)pTCBs[id];
        pNode->nid=pTCBs[id]->priority;
        push(pMFQ,pNode);
    }
}

void freethread()
{
    // 释放线程结点
    if(pActiveNode) {
        free(pActiveNode);
        pActiveNode=NULL;
    }
    // 释放TCB
    if(pActiveTCB) {
        free(pActiveTCB);
        pActiveTCB=NULL;
        pTCBs[active_thread]=NULL;
    }
    // 重置标志
    threads_h[active_thread]=NULL;
    flag[active_thread]=0;
}

void freeResourse()
{
    state=USER_STATE;
    for(int i=0; i<=MAX_THREAD_SIZE; i++) {
        if(threads_h[i]) {
            last_thread=-1;
            active_thread=i;
            ResumeThread(threads_h[active_thread]);
            while(threads_h[i]) {
                state=USER_STATE;
            }
        }
    }
    for(int i=0; i<=pMFQ->size; i++) {
        free(pMFQ->QueueTable[i]);
    }
    free(pMFQ);
}


void clock_interrupt()
{
    printInterrupt();
    state=KERNEL_STATE;
    /*
     * 用忙等待的方式模拟用户线程的阻塞
     * 表示等待操作系统处理中断完成
     */
    while(state!=USER_STATE) {
        // 等待变为用户态
    }
}

void interrupt_process()
{
    /*
     * 检查当前线程的时间片是否用完
     * 若没有用完，则检测有没有更高优先级的线程就绪
     * 若时间片已经用完，则调度其他线程运行
     */
    ElemNode *pNode=NULL;
    if(pTCBs[active_thread]->time_slice) {
        (pTCBs[active_thread]->time_slice)--;
        (pTCBs[active_thread]->run_time)++;
        // 时间片没有用完
        int hasHP=check_high_priority(pTCBs[active_thread]->priority);
        if(hasHP) {
            // 若有更高优先级的线程，则剥夺当前线程
            depriveCPU(active_thread);
            pActiveNode=dispatch(pMFQ);
            getCPU(pActiveNode); 
        } else {
            // 没有高优先级的线程
            // 则放回用户态下运行
            state=USER_STATE;
        }
    } else {
        // 时间片已经用完了
        timeOut(active_thread);
        pActiveNode=dispatch(pMFQ);
        getCPU(pActiveNode);
    }
}

void getCPU(ElemNode *pNode)
{
    if(pNode) {
        extern int priority_time_slices[MFQ_MAXSIZE+1];
        pActiveTCB=(TCB_t *)(pActiveNode->pEelm);
        pActiveTCB->priority=pActiveNode->nid;
        pActiveTCB->isDeprived=0;
        pActiveTCB->time_slice=priority_time_slices[pActiveNode->nid];
        last_thread=active_thread;
        active_thread=pActiveTCB->tid;
        ResumeThread(threads_h[active_thread]);
        state=USER_STATE;
    } else {
        // 当前MFQ中没有线程可调度
        // 则继续在核心态下运行
        state=KERNEL_STATE;
    }
}

void timeOut(int tid)
{
    SuspendThread(threads_h[tid]);
    /*
     * 时间片正常运行完后
     * 将优先级降到最低
     */
    pTCBs[tid]->priority=1;
    pActiveNode->nid=1;
    push(pMFQ,pActiveNode);
}

void depriveCPU(int tid)
{
    SuspendThread(threads_h[tid]);
    /*
    * 该线程刚刚运行
    * 但是被高优先级的线程剥夺
    * 这里采用的是将优先级减低一级的策略
    * 因此该线程加入到下一级队列的队尾
    */
    pTCBs[tid]->isDeprived = 1;
    pTCBs[tid]->priority -= 1;
    if(pTCBs[tid]->priority<1) {
        // 优先级不能小于1
        pTCBs[tid]->priority=1;
    }
    pActiveNode->nid=pTCBs[tid]->priority;
    push(pMFQ, pActiveNode);
}

int check_high_priority(int k) {
    return top_priority(pMFQ) > k ? 1 : 0;
}

int random(int low, int hi)
{
    if(low<hi) {
        srand( (unsigned)time(0) );
        return rand() % (hi-low+1) + low ;
    }
    return 0;
}
